/* 
 * File:   MyBatchMerge.cpp
 * Author: Morpheus
 * 
 * Created on 20 juin 2013, 16:15
 */

#include "MyBatchMerge.h"

#include <QtCore/qlist.h>
#include <QtCore/qalgorithms.h>
#include <QtCore/qdatastream.h>
#include <QtCore/qpair.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qdebug.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#include <QtCore/qdatetime.h>

const int MyBatchMerge::version = 2;

MyBatchMerge::MyBatchMerge()
    : m_id(0), m_created(QDateTime::currentDateTime()), m_lastModified(QDateTime::currentDateTime()),
      m_saveFileFormat("PDF"), m_saveFileOk(false), m_blockUpdate(false), m_doWthSucces(false) {
}
MyBatchMerge::MyBatchMerge(const MyBatchMerge& orig)
 : m_batchList(orig.m_batchList), m_id(orig.m_id), m_created(orig.m_created), m_lastModified(orig.m_lastModified), m_name(orig.m_name),
   m_saveFilePatern(orig.m_saveFilePatern), m_saveFileFormat(orig.m_saveFileFormat),
   m_saveFile(orig.m_saveFile), m_saveFileOk(orig.m_saveFileOk), m_blockUpdate(false), m_doWthSucces(orig.m_doWthSucces) {
}

MyBatchMerge::~MyBatchMerge() {
}

void MyBatchMerge::detach(){
    QList<MyBatchUnit> list;
    foreach(MyBatchUnit unit, m_batchList){
        unit.detach();
        list << unit;
    }
    m_batchList.clear();
    m_batchList = list;
}

bool MyBatchMerge::removeBatch(const MyBatchUnit& v){
    if(!m_batchList.contains(v))
        return false;
    
    m_batchList.removeOne(v);
    
    updateSaveFile();
    return true;
}
bool MyBatchMerge::insertBatch(const MyBatchUnit& v, const int& i){
    int idx = m_batchList.indexOf(v);
    
    if(idx != -1){
        m_batchList.replace(idx, v);
    }else{
        if(i < 0 || i >= m_batchList.size()){
            m_batchList << v;
        }else{
            m_batchList.insert(i, v);
        }
    }
    
    updateSaveFile();
    return true;
}
bool MyBatchMerge::swapBatch(int i1, int i2){
    if(i1 < 0 || i1 >= m_batchList.size() || i2 < 0 || i2 >= m_batchList.size())
        return false;
    
    qSwap(m_batchList[i1], m_batchList[i2]);
    updateSaveFile();
    return true;
}

QList<MyBatchUnit> MyBatchMerge::getBatchList() const {
    return m_batchList;
}

void MyBatchMerge::setBatchList(const QList<MyBatchUnit>& list) {
    m_batchList = list;
    updateSaveFile();
}

QString MyBatchMerge::getName() const {
    return m_name;
}

void MyBatchMerge::setName(const QString& name) {
    this->m_name = name;
    updateSaveFile();
}

QString MyBatchMerge::getSaveFile() const {
    return m_saveFile;
}

bool MyBatchMerge::isSaveFileOk() const {
    return m_saveFileOk;
}

QString MyBatchMerge::getSaveFilePatern() const {
    return m_saveFilePatern;
}

void MyBatchMerge::setSaveFilePatern(const QString& saveFilePatern) {
    this->m_saveFilePatern = saveFilePatern;
    updateSaveFile();
}

QString MyBatchMerge::getSaveFileFormat() const {
    return m_saveFileFormat;
}

void MyBatchMerge::setSaveFileFormat(const QString& saveFileFormat) {
    this->m_saveFileFormat = saveFileFormat;
}

bool MyBatchMerge::isBlockUpdate() const {
    return m_blockUpdate;
}

void MyBatchMerge::setBlockUpdate(const bool& blockUpdate) {
    this->m_blockUpdate = blockUpdate;
    updateSaveFile();
}

void MyBatchMerge::updateSaveFile() {
    if(m_blockUpdate)
        return;
    m_saveFileOk = true;        // func interpretPatern(...) modified m_saveFileOk
    m_saveFile = interpretPatern(m_saveFilePatern, m_saveFileOk);
}

int MyBatchMerge::getUUID() const{
    return m_id;
}
void MyBatchMerge::setUUID(const int& v){
    m_id = v;
}

QString MyBatchMerge::interpretPatern(const QString& patern, bool& bOk){
    static QChar begin('<');
    static QChar end('>');
    int count = patern.count(begin) + patern.count(end);
    if(count == 0){
        bOk = true;
        return patern;
    }
    
    if(patern.count(begin)*2 != count){
        bOk = false;
        return QString::fromUtf8("Paire '<' / '>' Invalide");
    }
    
    QStringList paternList;
    int len = 0;
    int idx = -1;
    int counTag = 0;
    int lastCounTag = 0;
    
    
    for (int i = 0; i < patern.size(); i++) {
        QChar cur = patern.at(i);
        lastCounTag = counTag;
        if(cur == begin){
            counTag += 1;
        }else if(cur == end){
            counTag -= 1;
        }
        if(counTag < 0){
            bOk = false;
            return QString::fromUtf8("Paire '<' / '>' Invalide");
        }
        
        if(idx == -1){
            idx = i;
            len = 0;
        }
        len += 1;
        
        if(counTag == 1 && lastCounTag == 0){ // debut de tag
            if(len > 1){
                paternList << patern.mid(idx, len-1);
            }
            idx = i;
            len = 0;
        }else if(counTag == 0 && lastCounTag == 1){  // fin de tag
            if(len > 0){
                paternList << patern.mid(idx, len+1);
            }
            idx = -1;
        }
    }
    if(idx != -1 && idx < patern.size()){
        if(counTag != 0){
            bOk = false;
            return QString::fromUtf8("Paire '<' / '>' Invalide");
        }
            
        paternList << patern.mid(idx);
    }
    
    QStringList result;
    foreach(QString subPatern, paternList){
        count = subPatern.count(begin) + subPatern.count(end);
        if(count == 0){
            result << subPatern;
        }else{
            subPatern = interpretFunc(subPatern, bOk);
            result << subPatern;
            if(!bOk){
                return subPatern;
            }
        }
    }

    return result.join("");
}

QString MyBatchMerge::interpretFunc(const QString& patern, bool& bOk){
    QString func = patern.mid(1, patern.size()-2);
    func = interpretPatern(func, bOk).trimmed();
    if(func.isEmpty()){
        bOk = false;
        return QString::fromUtf8("Fonction vide");
    }
    if(!bOk){
        return func;
    }
    QStringList param;
    {
        int idx = func.indexOf("(");
        if(idx < 1){
            bOk = false;
            return QString::fromUtf8("Fonction %1 : Paire '(' / ')' Invalide").arg(func);
        }
        int idx2 = func.lastIndexOf(")");
        if(idx2 < idx){
            bOk = false;
            return QString::fromUtf8("Fonction %1 : Paire '(' / ')' Invalide").arg(func);
        }
        param = func.mid(idx+1, idx2-idx-1).split(";", QString::SkipEmptyParts);
        func = func.left(idx).trimmed();
        
//        qDebug() << func << param;
    }
    
    QString result;
    do{
        if(func == "name"){
            result = m_name;
            bOk = true;
        }else if(func == "fileName"){
            if(param.isEmpty()){
                result = QString::fromUtf8("Fonction %1 necessite 1 paramètre").arg(func);
                break;
            }
            int idx = param.at(0).toInt(&bOk) -1;
            if(!bOk){
                result = QString::fromUtf8("Fonction %1 : Le Paramètre doit être un Nombre").arg(func);
                break;
            }
            if(idx < 0 || idx >= m_batchList.size()){
                result = QString::fromUtf8("Fonction 1% : Il n'y a pas d'élément à l'index %2").arg(func).arg(idx+1);
                break;
            }
            if(!m_batchList.at(idx).isFile()){
                result = QString::fromUtf8("Fonction 1% : L'élément à l'index %2 n'est pas un fichier").arg(func).arg(idx+1);
                break;
            }

            QFileInfo info = m_batchList.at(idx).getFileInfo();
            result = info.fileName();
            bOk = true;
        }else if(func == "fileBaseName"){
            if(param.isEmpty()){
                result = QString::fromUtf8("Fonction %1 necessite 1 paramètre").arg(func);
                break;
            }
            int idx = param.at(0).toInt(&bOk) -1;
            if(!bOk){
                result = QString::fromUtf8("Fonction %1 : Le Paramètre doit être un Nombre").arg(func);
                break;
            }
            if(idx < 0 || idx >= m_batchList.size()){
                result = QString::fromUtf8("Fonction 1% : Il n'y a pas d'élément à l'index %2").arg(func).arg(idx+1);
                break;
            }
            if(!m_batchList.at(idx).isFile()){
                result = QString::fromUtf8("Fonction 1% : L'élément à l'index %2 n'est pas un fichier").arg(func).arg(idx+1);
                break;
            }
            QFileInfo info = m_batchList.at(idx).getFileInfo();
            result = info.baseName();
            bOk = true;
        }else if(func == "fileSuffixe"){
            if(param.isEmpty()){
                result = QString::fromUtf8("Fonction %1 necessite 1 paramètre").arg(func);
                break;
            }
            int idx = param.at(0).toInt(&bOk) -1;
            if(!bOk){
                result = QString::fromUtf8("Fonction %1 : Le Paramètre doit être un Nombre").arg(func);
                break;
            }
            if(idx < 0 || idx >= m_batchList.size()){
                result = QString::fromUtf8("Fonction %1 : Il n'y a pas d'élément à l'index %2").arg(func).arg(idx+1);
                break;
            }
            if(!m_batchList.at(idx).isFile()){
                result = QString::fromUtf8("Fonction %1 : L'élément à l'index %2 n'est pas un fichier").arg(func).arg(idx+1);
                break;
            }
            QFileInfo info = m_batchList.at(idx).getFileInfo();
            result = info.suffix();
            bOk = true;
        }else if(func == "filePath"){
            if(param.isEmpty()){
                result = QString::fromUtf8("Fonction %1 necessite 1 paramètre").arg(func);
                break;
            }
            int idx = param.at(0).toInt(&bOk) -1;
            if(!bOk){
                result = QString::fromUtf8("Fonction %1 : Le Paramètre doit être un Nombre").arg(func);
                break;
            }
            if(idx < 0 || idx >= m_batchList.size()){
                result = QString::fromUtf8("Fonction %1 : Il n'y a pas d'élément à l'index %2").arg(func).arg(idx+1);
                break;
            }
            if(!m_batchList.at(idx).isFile()){
                result = QString::fromUtf8("Fonction %1 : L'élément à l'index %2 n'est pas un fichier").arg(func).arg(idx+1);
                break;
            }
            QFileInfo info = m_batchList.at(idx).getFileInfo();
            result = info.absolutePath();
            bOk = true;
        }else if(func == "fileCompletPath"){
            if(param.isEmpty()){
                result = QString::fromUtf8("Fonction %1 necessite 1 paramètre").arg(func);
                break;
            }
            int idx = param.at(0).toInt(&bOk) -1;
            if(!bOk){
                result = QString::fromUtf8("Fonction %1 : Le Paramètre doit être un Nombre").arg(func);
                break;
            }
            if(idx < 0 || idx >= m_batchList.size()){
                result = QString::fromUtf8("Fonction %1 : Il n'y a pas d'élément à l'index %2").arg(func).arg(idx+1);
                break;
            }
            if(!m_batchList.at(idx).isFile()){
                result = QString::fromUtf8("Fonction 1% : L'élément à l'index %2 n'est pas un fichier").arg(func).arg(idx+1);
                break;
            }
            QFileInfo info = m_batchList.at(idx).getFileInfo();
            result = info.absoluteFilePath();
            bOk = true;
        }else if(func == "dirName"){
            if(param.isEmpty()){
                result = QString::fromUtf8("Fonction %1 necessite 1 paramètre").arg(func);
                break;
            }
            QString tmp = param.at(0).trimmed();
            if(tmp.isEmpty()){
                result = QString::fromUtf8("Fonction %1 : Le paramètre est vide").arg(func);
                break;
            }
            QDir info(tmp);
            result = info.dirName();
            bOk = true;
        }else if(func == "cdUP"){
            if(param.isEmpty()){
                result = QString::fromUtf8("Fonction %1 necessite 1 paramètre").arg(func);
                break;
            }
            QString tmp = param.at(0).trimmed();
            if(tmp.isEmpty()){
                result = QString::fromUtf8("Fonction %1 : Le paramètre est vide").arg(func);
                break;
            }
            QDir info(tmp);
            if(!info.cdUp()){
                result = QString::fromUtf8("Fonction %1 : %2 n'a pas de dossier parent").arg(func).arg(tmp);
                break;
            }
            result = info.absolutePath();
            bOk = true;
        }else if(func == "fileCreated"){
            if(param.size() < 2){
                result = QString::fromUtf8("Fonction %1 necessite 2 paramètre").arg(func);
                break;
            }
            int idx = param.at(0).trimmed().toInt(&bOk) -1;
            if(!bOk){
                result = QString::fromUtf8("Fonction %1 : Le Paramètre #1 doit être un Nombre").arg(func);
                break;
            }
            if(idx < 0 || idx >= m_batchList.size()){
                result = QString::fromUtf8("Fonction 1% : Il n'y a pas d'élément à l'index %2").arg(func).arg(idx+1);
                break;
            }
            if(!m_batchList.at(idx).isFile()){
                result = QString::fromUtf8("Fonction 1% : L'élément à l'index %2 n'est pas un fichier").arg(func).arg(idx+1);
                break;
            }
            QString tmp = param[1].trimmed();

            QFileInfo info = m_batchList.at(idx).getFileInfo();
            QDateTime dt = info.created();
            if(tmp == "Text"){
                result = dt.toString(Qt::TextDate);
            }else if(tmp == "ISO"){
                result = dt.toString(Qt::ISODate);
            }else{
                result = dt.toString(param[1]);
            }
            bOk = true;
        }else if(func == "fileModified"){
            if(param.size() < 2){
                result = QString::fromUtf8("Fonction %1 necessite 2 paramètre").arg(func);
                break;
            }
            int idx = param.at(0).trimmed().toInt(&bOk) -1;
            if(!bOk){
                result = QString::fromUtf8("Fonction %1 : Le Paramètre #1 doit être un Nombre").arg(func);
                break;
            }
            if(idx < 0 || idx >= m_batchList.size()){
                result = QString::fromUtf8("Fonction 1% : Il n'y a pas d'élément à l'index %2").arg(func).arg(idx+1);
                break;
            }
            if(!m_batchList.at(idx).isFile()){
                result = QString::fromUtf8("Fonction 1% : L'élément à l'index %2 n'est pas un fichier").arg(func).arg(idx+1);
                break;
            }
            QString tmp = param[1].trimmed();

            QFileInfo info = m_batchList.at(idx).getFileInfo();
            QDateTime dt = info.lastModified();
            if(tmp == "Text"){
                result = dt.toString(Qt::TextDate);
            }else if(tmp == "ISO"){
                result = dt.toString(Qt::ISODate);
            }else{
                result = dt.toString(param[1]);
            }
            bOk = true;
        }else if(func == "endl"){
            result = "\n\r";
            bOk = true;
        }else if(func == "mid"){
            if(param.size() < 2){
                result = QString::fromUtf8("Fonction %1 necessite au moins 2 paramètre").arg(func);
                break;
            }
            int idx = param.at(1).trimmed().toInt(&bOk);
            if(!bOk){
                result = QString::fromUtf8("Fonction %1 : Le Paramètre #2 doit être un Nombre").arg(func);
                break;
            }
            QString tmp = param.at(0);
            if(idx < 0 || idx >= tmp.size()){
                result = QString::fromUtf8("Fonction %1 : '%2' (Paramètre#1) n'a pas de caractère à l'index %3 (Paramètre#2)").arg(func).arg(tmp).arg(idx);
                break;
            }
            int len = -1;
            if(param.size() > 2){
                len = param.at(2).trimmed().toInt(&bOk);
                if(!bOk){
                    result = QString::fromUtf8("Fonction %1 : Le Paramètre #3 doit être un Nombre").arg(func);
                    break;
                }
            }
            if(len < -1){
                result = QString::fromUtf8("Fonction %1 : Le Paramètre #3 (%2)in invalide").arg(func).arg(len);
                break;
            }
            result = tmp.mid(idx, len);
            bOk = true;
        }else if(func == "parseDate"){
            if(param.size() < 3){
                result = QString::fromUtf8("Fonction %1 necessite 3 paramètre").arg(func);
                break;
            }
            QDateTime dt = QDateTime::fromString(param.at(0), param.at(1));
            if(!dt.isValid()){
                result = QString::fromUtf8("Fonction %1 : impossible de trouver trouvé la date dans %2 avec le masque %3").arg(func).arg(param.at(0)).arg(param.at(1));
                break;
            }
            QString tmp = param.at(2).trimmed();
            if(tmp == "Text"){
                result = dt.toString(Qt::TextDate);
            }else if(tmp == "ISO"){
                result = dt.toString(Qt::ISODate);
            }else{
                result = dt.toString(param[2]);
            }
            bOk = true;
        }else if(func == "parseDateReg"){
            if(param.size() < 3){
                result = QString::fromUtf8("Fonction %1 necessite 3 paramètre").arg(func);
                break;
            }
            QString patern;
            char curChar = -1;
            int countCurChar = 1;
            QString tmp = param.at(1);
            for(int i = 0; i < tmp.size(); i++){
                char c = tmp.at(i).toLatin1();
                if(c == curChar){
                    countCurChar += 1;
                }else if(curChar == -1){
                    curChar = c;
                }
                if(c != curChar || i == tmp.size()-1){
                    switch (curChar) {
                    case 'd':
                    case 'M':
                        if(countCurChar < 3){
                            patern += QString("(\\d{1,%1})").arg(countCurChar);
                        }else{
                            patern += "(\\w+)";
                        }
                        break;
                    case 'h':
                    case 'H':
                    case 'y':
                    case 'm':
                    case 's':
                        patern += QString("(\\d{1,%1})").arg(countCurChar);
                        break;
                    case 'a':
                    case 'A':
                        patern += "(am|pm)";
                        break;
                    default:
                        patern += ".";
                        break;
                    }

                    curChar = c;
                    countCurChar = 1;
                }
            }
            QString txt = param.at(0);
            QRegExp rx(patern, Qt::CaseInsensitive/*, QRegExp::FixedString*/);
            int idx = rx.indexIn(txt);
            int len = 0;
            if(idx != -1){
                len = rx.matchedLength();
            }
            if(idx == -1 || len == 0){
                result = QString::fromUtf8("Fonction %1 : impossible de trouver la date dans %2 avec le masque %3 (rx = %4)").arg(func).arg(txt).arg(tmp).arg(patern);
                break;
            }
            txt = txt.mid(idx, len);
            QDateTime dt = QDateTime::fromString(txt, tmp);
            if(!dt.isValid()){
                result = QString::fromUtf8("Fonction %1 : la date trouvé dans %2 avec le masque %3 est invalide").arg(func).arg(txt).arg(tmp);
                break;
            }
            tmp = param.at(2).trimmed();
            if(tmp == "Text"){
                result = dt.toString(Qt::TextDate);
            }else if(tmp == "ISO"){
                result = dt.toString(Qt::ISODate);
            }else{
                result = dt.toString(param[2]);
            }
            bOk = true;
        }else{
            bOk = false;
            result = QString::fromUtf8("Fonction %1(%2) inconnue").arg(func).arg(param.join(";"));
        }
    }while (false);
    
    return result;
}

bool MyBatchMerge::getDoWithSuccess() const{
    return m_doWthSucces;
}
void MyBatchMerge::setDoWithSuccess(const bool& b){
    m_doWthSucces = b;
    if(!b) // signifie modif enregistre mais pas traiter
        m_lastModified = QDateTime::currentDateTime();
}
QDateTime MyBatchMerge::getCreatedDate() const{
    return m_created;
}
void MyBatchMerge::setCreatedDate(const QDateTime& d){
    m_created = d;
}

QDateTime MyBatchMerge::getLastModifiedDate() const{
    return m_lastModified;
}
void MyBatchMerge::setLastModifiedDate(const QDateTime& d){
    m_lastModified = d;
}

QDataStream& operator<<(QDataStream& s, const MyBatchMerge& obj){
    s << MyBatchMerge::version << QString("MyBatchMerge");
    s << obj.getUUID();
    s << obj.getName();
    s << obj.getSaveFilePatern();
    s << obj.getSaveFileFormat();
    s << obj.getDoWithSuccess();
    s << obj.getCreatedDate();
    s << obj.getLastModifiedDate();
    s << obj.getBatchList();
    return s;
}
QDataStream& operator>>(QDataStream& s, MyBatchMerge& obj){
    obj.setBlockUpdate(true);
    QString tmp;        int id = 0;        int version = 0;
    s >> version >> tmp;
    if(tmp != QString("MyBatchMerge")){
        s.setStatus(QDataStream::ReadCorruptData);
        return s;
    }
    s >> id;    obj.setUUID(id);
    s >> tmp;    obj.setName(tmp);
    s >> tmp;    obj.setSaveFilePatern(tmp);
    s >> tmp;    obj.setSaveFileFormat(tmp);
    bool b = false;
    s >> b;     obj.setDoWithSuccess(b);
    QDateTime t;
    s >> t;     obj.setCreatedDate(t);
    s >> t;     obj.setLastModifiedDate(t);
    QList<MyBatchUnit> list;
    s >> list;
    obj.setBatchList(list);
    
    obj.setBlockUpdate(false);
    return s;
}
